import 'dart:convert';
import 'package:json_annotation/json_annotation.dart';

part 'ip_utils_options.g.dart';

@JsonSerializable()
class IpUtilsOptions {
  //==== IPv4 Input Options ===================================================
  LeadingZerosMode ipv4InputAddressLeadingZerosMode;
  LeadingZerosMode ipv4InputMaskLeadingZerosMode;
  LeadingZerosMode ipv4InputCidrLeadingZerosMode;
  LeadingZerosMode ipv4InputPortLeadingZerosMode;

  //==== IPv4 Output Options ==================================================
  LeadingZerosMode ipv4OutputAddressLeadingZerosMode;
  LeadingZerosMode ipv4OutputMaskLeadingZerosMode;
  LeadingZerosMode ipv4OutputCidrLeadingZerosMode;
  LeadingZerosMode ipv4OutputPortLeadingZerosMode;

  //==== IPv6 Input Options ===================================================
  LeadingZerosMode ipv6InputAddressLeadingZerosMode;
  LeadingZerosMode ipv6InputCidrLeadingZerosMode;
  LeadingZerosMode ipv6InputPortLeadingZerosMode;

  CaseMode ipv6InputCaseMode;

  /// ### English
  /// If true, allows IPv4-mapped IPv6 addresses to be treated as IPv6 addresses.<br>
  /// For example, if true,<br>
  /// ::ffff:192.0.2.128 or 0:0:0:0:0:ffff:192.0.2.128 is a valid IPv6 address.
  ///
  /// ---
  ///
  /// ### 中文(Chinese)
  /// 如果值为 true，则允许将 IPv4-mapped IPv6 地址视为 IPv6 地址。<br>
  /// 例如，如果值为 true，<br>
  /// ::ffff:192.0.2.128 是一个有效的 IPv6 地址。
  bool ipv6InputIpv4MappedIpv6IsIpv6;

  //==== IPv6 Output Options ==================================================
  LeadingZerosMode ipv6OutputAddressLeadingZerosMode;
  LeadingZerosMode ipv6OutputCidrLeadingZerosMode;
  LeadingZerosMode ipv6OutputPortLeadingZerosMode;

  /// ### English
  /// If true, outputs compressed IPv6 addresses.<br>
  /// If false, outputs full IPv6 addresses.
  ///
  /// ---
  ///
  /// ### 中文(Chinese)
  /// 如果值为 true，则输出压缩的 IPv6 地址。<br>
  /// 如果值为 false，则输出完整的 IPv6 地址。
  bool ipv6OutputCompressed;

  CaseMode ipv6OutputCaseMode;

  IpUtilsOptions({
    // IPv4 input leading zeros
    this.ipv4InputAddressLeadingZerosMode = LeadingZerosMode.excluded,
    this.ipv4InputMaskLeadingZerosMode = LeadingZerosMode.excluded,
    this.ipv4InputCidrLeadingZerosMode = LeadingZerosMode.excluded,
    this.ipv4InputPortLeadingZerosMode = LeadingZerosMode.excluded,
    // IPv4 output leading zeros
    this.ipv4OutputAddressLeadingZerosMode = LeadingZerosMode.excluded,
    this.ipv4OutputMaskLeadingZerosMode = LeadingZerosMode.excluded,
    this.ipv4OutputCidrLeadingZerosMode = LeadingZerosMode.excluded,
    this.ipv4OutputPortLeadingZerosMode = LeadingZerosMode.excluded,
    // IPv6 input leading zeros
    this.ipv6InputAddressLeadingZerosMode = LeadingZerosMode.excluded,
    this.ipv6InputCidrLeadingZerosMode = LeadingZerosMode.excluded,
    this.ipv6InputPortLeadingZerosMode = LeadingZerosMode.excluded,
    // IPv6 input others
    this.ipv6InputCaseMode = CaseMode.lowercase,
    this.ipv6InputIpv4MappedIpv6IsIpv6 = true,
    // IPv6 output leading zeros
    this.ipv6OutputAddressLeadingZerosMode = LeadingZerosMode.excluded,
    this.ipv6OutputCidrLeadingZerosMode = LeadingZerosMode.excluded,
    this.ipv6OutputPortLeadingZerosMode = LeadingZerosMode.excluded,
    // IPv6 output others
    this.ipv6OutputCompressed = true,
    this.ipv6OutputCaseMode = CaseMode.lowercase,
  });

  /// ### English
  /// Default options for IPv4/IPv6 input/output.
  ///
  /// [https://www.rfc-editor.org/rfc/rfc5952#section-4](https://www.rfc-editor.org/rfc/rfc5952#section-4)
  ///
  /// ```plaintext
  /// 4.  A Recommendation for IPv6 Text Representation
  ///
  ///    A recommendation for a canonical text representation format of IPv6
  ///    addresses is presented in this section.  The recommendation in this
  ///    document is one that complies fully with [RFC4291], is implemented by
  ///    various operating systems, and is human friendly.  The recommendation
  ///    in this section SHOULD be followed by systems when generating an
  ///    address to be represented as text, but all implementations MUST
  ///    accept and be able to handle any legitimate [RFC4291] format.  It is
  ///    advised that humans also follow these recommendations when spelling
  ///    an address.
  ///
  /// 4.1.  Handling Leading Zeros in a 16-Bit Field
  ///
  ///    Leading zeros MUST be suppressed.  For example, 2001:0db8::0001 is
  ///    not acceptable and must be represented as 2001:db8::1.  A single 16-
  ///    bit 0000 field MUST be represented as 0.
  ///
  /// 4.2.  "::" Usage
  ///
  /// 4.2.1.  Shorten as Much as Possible
  ///
  ///    The use of the symbol "::" MUST be used to its maximum capability.
  ///    For example, 2001:db8:0:0:0:0:2:1 must be shortened to 2001:db8::2:1.
  ///    Likewise, 2001:db8::0:1 is not acceptable, because the symbol "::"
  ///    could have been used to produce a shorter representation 2001:db8::1.
  ///
  /// 4.2.2.  Handling One 16-Bit 0 Field
  ///
  ///    The symbol "::" MUST NOT be used to shorten just one 16-bit 0 field.
  ///    For example, the representation 2001:db8:0:1:1:1:1:1 is correct, but
  ///    2001:db8::1:1:1:1:1 is not correct.
  ///
  /// 4.2.3.  Choice in Placement of "::"
  ///
  ///    When there is an alternative choice in the placement of a "::", the
  ///    longest run of consecutive 16-bit 0 fields MUST be shortened (i.e.,
  ///    the sequence with three consecutive zero fields is shortened in 2001:
  ///    0:0:1:0:0:0:1).  When the length of the consecutive 16-bit 0 fields
  ///    are equal (i.e., 2001:db8:0:0:1:0:0:1), the first sequence of zero
  ///    bits MUST be shortened.  For example, 2001:db8::1:0:0:1 is correct
  ///    representation.
  ///
  /// 4.3.  Lowercase
  ///
  ///    The characters "a", "b", "c", "d", "e", and "f" in an IPv6 address
  ///    MUST be represented in lowercase.
  /// ```
  ///
  /// ---
  ///
  /// ### 中文(Chinese)
  /// IPv4/IPv6 输入/输出的默认选项。
  IpUtilsOptions.rfc5952() : this();

  factory IpUtilsOptions.anyInputAndRfc5952Output() {
    return IpUtilsOptions.leadingZerosMode(
      ipv4Input: LeadingZerosMode.any,
      ipv4Output: LeadingZerosMode.excluded,
      ipv6Input: LeadingZerosMode.any,
      ipv6Output: LeadingZerosMode.excluded,
    );
  }

  IpUtilsOptions.leadingZerosMode({
    LeadingZerosMode ipv4Input = LeadingZerosMode.excluded,
    LeadingZerosMode ipv4Output = LeadingZerosMode.excluded,
    LeadingZerosMode ipv6Input = LeadingZerosMode.excluded,
    LeadingZerosMode ipv6Output = LeadingZerosMode.excluded,
    CaseMode ipv6InputCaseMode = CaseMode.any,
    CaseMode ipv6OutputCaseMode = CaseMode.lowercase,
    bool ipv6InputIpv4MappedIpv6IsIpv6 = true,
    bool ipv6OutputCompressed = true,
  }) : this(
    // ipv4Input leading zeros
    ipv4InputAddressLeadingZerosMode: ipv4Input,
    ipv4InputMaskLeadingZerosMode: ipv4Input,
    ipv4InputCidrLeadingZerosMode: ipv4Input,
    ipv4InputPortLeadingZerosMode: ipv4Input,
    // ipv4Output leading zeros
    ipv4OutputAddressLeadingZerosMode: ipv4Output,
    ipv4OutputMaskLeadingZerosMode: ipv4Output,
    ipv4OutputCidrLeadingZerosMode: ipv4Output,
    ipv4OutputPortLeadingZerosMode: ipv4Output,
    // ipv6Input leading zeros
    ipv6InputAddressLeadingZerosMode: ipv6Input,
    ipv6InputCidrLeadingZerosMode: ipv6Input,
    ipv6InputPortLeadingZerosMode: ipv6Input,
    // ipv6Output leading zeros
    ipv6OutputAddressLeadingZerosMode: ipv6Output,
    ipv6OutputCidrLeadingZerosMode: ipv6Output,
    ipv6OutputPortLeadingZerosMode: ipv6Output,
    // ipv6 others
    ipv6InputCaseMode: ipv6InputCaseMode,
    ipv6InputIpv4MappedIpv6IsIpv6: ipv6InputIpv4MappedIpv6IsIpv6,
    ipv6OutputCompressed: ipv6OutputCompressed,
    ipv6OutputCaseMode: ipv6OutputCaseMode,
  );

  factory IpUtilsOptions.fromJsonMap(Map<String, dynamic> json) =>
      _$IpUtilsOptionsFromJson(json);

  factory IpUtilsOptions.fromJson(String json) =>
      IpUtilsOptions.fromJsonMap(jsonDecode(json));

  Map<String, dynamic> toJsonMap() => _$IpUtilsOptionsToJson(this);

  String toJson() => json.encode(toJsonMap());

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
          other is IpUtilsOptions &&
              runtimeType == other.runtimeType &&
              ipv4InputAddressLeadingZerosMode ==
                  other.ipv4InputAddressLeadingZerosMode &&
              ipv4InputMaskLeadingZerosMode ==
                  other.ipv4InputMaskLeadingZerosMode &&
              ipv4InputCidrLeadingZerosMode ==
                  other.ipv4InputCidrLeadingZerosMode &&
              ipv4InputPortLeadingZerosMode ==
                  other.ipv4InputPortLeadingZerosMode &&
              ipv4OutputAddressLeadingZerosMode ==
                  other.ipv4OutputAddressLeadingZerosMode &&
              ipv4OutputMaskLeadingZerosMode ==
                  other.ipv4OutputMaskLeadingZerosMode &&
              ipv4OutputCidrLeadingZerosMode ==
                  other.ipv4OutputCidrLeadingZerosMode &&
              ipv4OutputPortLeadingZerosMode ==
                  other.ipv4OutputPortLeadingZerosMode &&
              ipv6InputAddressLeadingZerosMode ==
                  other.ipv6InputAddressLeadingZerosMode &&
              ipv6InputCidrLeadingZerosMode ==
                  other.ipv6InputCidrLeadingZerosMode &&
              ipv6InputPortLeadingZerosMode ==
                  other.ipv6InputPortLeadingZerosMode &&
              ipv6InputCaseMode == other.ipv6InputCaseMode &&
              ipv6InputIpv4MappedIpv6IsIpv6 ==
                  other.ipv6InputIpv4MappedIpv6IsIpv6 &&
              ipv6OutputAddressLeadingZerosMode ==
                  other.ipv6OutputAddressLeadingZerosMode &&
              ipv6OutputCidrLeadingZerosMode ==
                  other.ipv6OutputCidrLeadingZerosMode &&
              ipv6OutputPortLeadingZerosMode ==
                  other.ipv6OutputPortLeadingZerosMode &&
              ipv6OutputCompressed == other.ipv6OutputCompressed &&
              ipv6OutputCaseMode == other.ipv6OutputCaseMode;

  @override
  int get hashCode =>
      ipv4InputAddressLeadingZerosMode.hashCode ^
      ipv4InputMaskLeadingZerosMode.hashCode ^
      ipv4InputCidrLeadingZerosMode.hashCode ^
      ipv4InputPortLeadingZerosMode.hashCode ^
      ipv4OutputAddressLeadingZerosMode.hashCode ^
      ipv4OutputMaskLeadingZerosMode.hashCode ^
      ipv4OutputCidrLeadingZerosMode.hashCode ^
      ipv4OutputPortLeadingZerosMode.hashCode ^
      ipv6InputAddressLeadingZerosMode.hashCode ^
      ipv6InputCidrLeadingZerosMode.hashCode ^
      ipv6InputPortLeadingZerosMode.hashCode ^
      ipv6InputCaseMode.hashCode ^
      ipv6InputIpv4MappedIpv6IsIpv6.hashCode ^
      ipv6OutputAddressLeadingZerosMode.hashCode ^
      ipv6OutputCidrLeadingZerosMode.hashCode ^
      ipv6OutputPortLeadingZerosMode.hashCode ^
      ipv6OutputCompressed.hashCode ^
      ipv6OutputCaseMode.hashCode;
}

/// ### English
/// IPv4/IPv6 address, mask, CIDR, and port leading zeros mode
///
/// ---
///
/// ### 中文(Chinese)
/// IPv4/IPv6地址, 掩码, CIDR, 端口的前导零模式
enum LeadingZerosMode {
  /// ## English
  /// It can be with or without leading zeros.<br>
  ///
  /// For example,111.111.111.111, 0.0.0.0 , 00.00.00.00 and 000.000.000.000 are all valid.
  ///
  /// ---
  ///
  /// ## 中文(Chinese)
  /// 可以有前导零,也可以没有前导零. <br>
  ///
  /// 例如, 111.111.111.111, 0.0.0.0 , 00.00.00.00 和 000.000.000.000 都是有效的.
  any,

  /// ## English
  /// Leading zeros must be included. <br>
  ///
  /// For example,111.111.111.111 000.000.000.000 and 00.00.00.00 are valid,<br>
  /// but 0.0.0.0 is not valid.
  ///
  /// ---
  ///
  /// ## 中文(Chinese)
  /// 必须包含前导零. <br>
  ///
  /// 例如,111.111.111.111 000.000.000.000 和 00.00.00.00 都是有效的,<br>
  /// 0.0.0.0 不是有效的.
  included,

  /// ## English
  /// Leading zeros must be included completely. <br>
  ///
  /// For example, 0.0.0.0 and 00.00.00.00 are not valid,<br>
  /// but 111.111.111.111 and  000.000.000.000 is valid.
  ///
  /// ---
  ///
  /// ## 中文(Chinese)
  /// 必须包含完整的前导零. <br>
  ///
  /// 比如, 0.0.0.0 和 00.00.00.00 不是有效的,<br>
  /// 111.111.111.111 和 000.000.000.000 是有效的.
  ///
  /// 此选项可以用来验证IPv4地址的长度是否完整.
  includedCompletely,

  /// ## English
  /// There must be no leading zeros.<br>
  ///
  /// For example, 000.000.000.000 and 00.00.00.00 are not valid,<br>
  /// but 111.111.111.111 and 0.0.0.0 is valid.
  ///
  /// ---
  ///
  /// ## 中文(Chinese)
  /// 必须不能有前导零. <br>
  ///
  /// 例如, 000.000.000.000 和 00.00.00.00 都不是有效的,<br>
  /// 111.111.111.111 和 0.0.0.0 是有效的.
  excluded
}

/// ## English
/// The case mode of an IPv6 address
///
/// ---
///
/// ## 中文(Chinese)
/// IPv6 地址的大小写模式
enum CaseMode {
  /// ## English
  /// It can be lowercase, uppercase, or a mix of case
  ///
  /// ---
  ///
  /// ## 中文(Chinese)
  /// 可以是小写,也可以是大写,也可以是大小写混合
  any,

  /// ## English
  /// Must be lowercase
  ///
  /// ---
  ///
  /// ## 中文(Chinese)
  /// 必须是小写
  lowercase,

  /// ## English
  /// Must be uppercase
  ///
  /// ---
  ///
  /// ## 中文(Chinese)
  /// 必须是大写
  uppercase
}

/// ## English
/// IPv6 address and port separator
///
/// ---
///
/// ## 中文(Chinese)
/// IPv6 地址和端口的分隔符
enum Ipv6PortSeparator {
  /// ## English
  /// The separator is "[]:".<br>
  /// RFC5952 recommends using "[]:" separator for IPv6 address and port. <br>
  /// For example, [2001:db8::1]:80
  /// ---
  ///
  /// ## 中文(Chinese)
  /// 分隔符是 "[]:"。<br>
  /// RFC5952 推荐使用 "[]:" 分隔 IPv6 地址和端口。<br>
  /// 例如, [2001:db8::1]:80
  squareBracketColon('[]:'),

  /// ## English
  /// The separator is ":".<br>
  /// RFC5952 NOT recommends using ":" separator for IPv6 address and port. <br>
  /// For example, 2001:db8::1:80
  ///
  /// ---
  ///
  /// ## 中文(Chinese)
  /// 分隔符是 ":"。<br>
  /// RFC5952 不推荐使用 ":" 分隔 IPv6 地址和端口。<br>
  /// 例如, 2001:db8::1:80
  colon(':'),

  /// ## English
  /// The separator is ".".<br>
  /// For example, 2001:db8::1.80
  ///
  /// ---
  ///
  /// ## 中文(Chinese)
  /// 分隔符是 "."。<br>
  /// 例如, 2001:db8::1.80
  dot('.'),

  /// ## English
  /// The separator is " port ".<br>
  /// For example, 2001:db8::1 port 80
  ///
  /// ---
  ///
  /// ## 中文(Chinese)
  /// 分隔符是 " port "。<br>
  /// 例如, 2001:db8::1 port 80
  spacePort(' port '),

  /// ## English
  /// The separator is "p".<br>
  /// For example, 2001:db8::1p80
  ///
  /// ---
  ///
  /// ## 中文(Chinese)
  /// 分隔符是 "p"。<br>
  /// 例如, 2001:db8::1p80
  p('p'),

  /// ## English
  /// The separator is "#".<br>
  /// For example, 2001:db8::1#80
  ///
  /// ---
  ///
  /// ## 中文(Chinese)
  /// 分隔符是 "#"。<br>
  /// 例如, 2001:db8::1#80
  hash('#');

  final String separator;

  const Ipv6PortSeparator(this.separator);

  @override
  String toString() => separator;

  static Ipv6PortSeparator? fromString(String separator) {
    for (var value in values) {
      if (value.separator == separator) {
        return value;
      }
    }
    return null;
  }

  static const Ipv6PortSeparator defaultSeparator = squareBracketColon;
}
